// 安装浏览器扩展如 "CORS Unblock" 或 "Allow CORS: Access-Control-Allow-Origin" 解决下载文件跨域问题

class LogParser {
    constructor() {
        this.levelRegex = /(TRACE|DEBUG|INFO|WARN|ERROR|FATAL)/;
        this.remainingBuffer = '';
    }

    parseChunk(chunk, startLine, isLastChunk = false) {
        const text = this.remainingBuffer + chunk;
        this.remainingBuffer = '';

        if (isLastChunk) {
            return this.parseCompleteLines(text, startLine, true);
        }

        const lines = text.split('\n');
        this.remainingBuffer = lines.pop() || '';

        return this.parseCompleteLines(lines.join('\n'), startLine);
    }


    parseCompleteLines(text, startLine, isLastChunk = false) {
        const lines = text.split('\n');
        const parsed = [];

        lines.forEach((line, i) => {
            if (!line.trim()) return;

            // 提取线程名（优先从管道符后获取）
            let thread = extractThreadName.call(this, line);


            // 提取日志级别
            const levelMatch = line.match(this.levelRegex);

            const level = levelMatch ? levelMatch[0] : 'INFO';

            const isLastLine = isLastChunk && i === lines.length - 1;

            parsed.push({
                content: line, thread, level, lineNumber: startLine + i, isLastLine
            });
        });

        return parsed;
    }
}

// 提取线程名（最终优化版）
// 提取线程名（优化版）
function extractThreadName(line) {
    // 1. 优先处理带方括号的线程名格式（Spring Boot 常见格式）
    const springBootThreadMatch = line.match(/\[([^\]]+)\]/g);
    if (springBootThreadMatch && springBootThreadMatch.length > 0) {
        // 尝试从最后一个方括号中提取（通常是线程名）
        for (let i = springBootThreadMatch.length - 1; i >= 0; i--) {
            const candidate = springBootThreadMatch[i].replace(/[\[\]]/g, '').trim();
            if (isValidThreadName(candidate)) {
                return candidate;
            }
        }
    }

    // 2. 处理管道分隔格式：时间戳|级别|线程名|类名...
    if (line.includes('|')) {
        const parts = line.split('|').map(p => p.trim());

        // 尝试从各个部分提取线程名
        for (let i = 1; i < Math.min(parts.length, 4); i++) {
            if (isValidThreadName(parts[i])) {
                return parts[i];
            }
        }
    }

    // 3. 尝试匹配常见线程名模式
    const threadPatterns = [// Spring Boot 格式：--- [thread-name]
        /---\s+\[([^\]]+)\]/,

        // 标准线程格式: Thread-1, main
        /(?:^|\s)(Thread-\d+|main)(?=\s|$)/i,

        // 线程池格式: pool-1-thread-1, http-nio-9001-exec-4
        /(?:^|\s)((?:[a-zA-Z]+-)?(?:pool|exec|worker|task|thread|nio|http)[\w-]*\d+)(?=\s|$)/i,

        // Netty/网络相关线程
        /(?:^|\s)((?:nio|epoll|netty)[\w-]+-\d+-\d+)(?=\s|$)/i,

        // Armeria框架线程格式
        /(?:^|\s)(armeria-[\w-]+-\d+-\d+)(?=\s|$)/i,

        // 通用带数字的线程名
        /(?:^|\s)([a-z]+-[\w-]*\d+-\d+)(?=\s|$)/i];

    for (const pattern of threadPatterns) {
        const match = line.match(pattern);
        if (match && match[1]) {
            const candidate = match[1].trim();
            if (isValidThreadName(candidate)) {
                return candidate;
            }
        }
    }

    return "unknown";
}

// 辅助函数：验证是否为有效的线程名
function isValidThreadName(name) {
    if (!name || name.length < 2 || name.length > 50) return false;
    if (name === "unknown" || name === "main") return true;

    // 排除常见非线程名的内容
    const invalidPatterns = [/^\d+$/, // 纯数字
        /^[A-Z]+$/, // 纯大写字母（如DEBUG, INFO）
        /^\d+\.\d+\.\d+\.\d+$/, // IP地址
        /^[0-9a-fA-F:-]+$/, // 看起来像ID或哈希值
        /^[,.;:!?]+$/ // 标点符号
    ];

    for (const pattern of invalidPatterns) {
        if (pattern.test(name)) return false;
    }

    return /^[a-zA-Z][\w-]*$/.test(name);
}

function isValidThreadName(name) {
    if (!name || name.length > 50) return false;

    // 绝对排除模式（新增CDATA、URL等排除项）
    const blacklistPatterns = [/^CDATA\[/i,            // 排除CDATA开头
        /^https?:\/\//i,        // 排除URL
        /^[a-z0-9]{32}$/i,      // 排除MD5等哈希值
        /\.(xml|json|html)$/i,  // 排除文件扩展名
        /[\u4e00-\u9fa5]/,      // 排除中文字符
        /[<>{}[\]]/             // 排除特殊符号
    ];

    // 允许的线程名格式
    const whitelistPatterns = [/^(http|https|dubbo|nio|pool|exec|thread|worker|armeria)-[\w-]+\d+/i, // 标准线程格式
        /^(?:[a-zA-Z]+_)?(?:Thread|main|gc|finalizer)(?:-\d+)?$/i,

        // 线程池/工作线程格式（支持_和-）
        /^(?:[a-zA-Z]+[_\-])?(?:pool|exec|worker|task|thread|scheduler)[_\-]\d+/i,

        // 框架特定线程（支持quartzScheduler_Worker-8）
        /^(?:quartz|armeria|netty|dubbo)[a-zA-Z]*[_\-][\w\-]+\d+/i,

        // 通用带数字的线程名
        /^[a-zA-Z]+[_\-][a-zA-Z]*\d+/i];

    // 先检查黑名单
    if (blacklistPatterns.some(p => p.test(name))) {
        return false;
    }

    // 再检查白名单
    return whitelistPatterns.some(p => p.test(name));
}

class LogViewer {
    constructor() {
        this.file = null;
        this.parser = new LogParser();
        this.logData = [];
        this.filteredData = [];
        this.threadMap = new Map();
        this.levelCounts = {
            TRACE: 0, DEBUG: 0, INFO: 0, WARN: 0, ERROR: 0, FATAL: 0
        };
        this.currentLevel = 'all'; // 添加当前级别状态
        this.contextLines = 5;// 添加上下文行数状态

        this.visibleLines = 50;
        this.lineHeight = 20;//日志显示行高
        this.renderBuffer = 20;

        // 添加滚动状态跟踪
        this.isScrolling = false;
        this.lastScrollTop = 0;
        this.lastRenderTime = 0;
        this.renderQueue = null;

        this.currentSearch = '';
        this.currentThread = 'all';
        this.currentThreadSearch = '';


        this.initResizable();

        // 新增模态框元素
        this.pasteModal = document.getElementById('pasteModal');
        this.pasteArea = document.getElementById('pasteArea');
        this.pasteBtn = document.getElementById('pasteBtn');
        this.submitPaste = document.getElementById('submitPaste');
        this.closeModal = document.querySelector('.close');

        this.socket = null;
        this.connectBtn = document.getElementById('connectBtn');
        this.serverUrl = document.getElementById('serverUrl');
        this.initWebSocket();


        this.initPasteEvents();

        this.initElements();
        this.initEvents();


        // 添加折叠框相关属性
        this.foldableCount = 0;
        this.foldableData = []; // 存储每个折叠框的数据

        // 新增下载相关属性
        this.downloadConfigs = [];
        this.currentDownloadConfig = null;
        // 初始化下载相关元素
        this.downloadSelect = document.getElementById('downloadSelect');
        this.downloadBtn = document.getElementById('downloadBtn');
        this.manageDownloadBtn = document.getElementById('manageDownloadBtn');
        this.downloadConfigModal = document.getElementById('downloadConfigModal');
        this.saveConfigBtn = document.getElementById('saveConfigBtn');
        this.configAlias = document.getElementById('configAlias');
        this.configUrl = document.getElementById('configUrl');
        this.downloadConfigList = document.getElementById('downloadConfigList');
        this.downloadProgress = document.createElement('div');
        this.downloadProgress.className = 'download-progress';
        document.querySelector('.log-container').appendChild(this.downloadProgress);

        // 初始化下载事件
        this.initDownloadEvents();
        this.loadDownloadConfigs();


        //全屏
        this.initFullscreen();


        // 初始化存储事件
        this.initStorageEvents();
        this.renderStorageList(); // 初始化时也渲染一次


    }

    // 添加创建折叠框的方法
    createFoldableBox(title, content) {
        this.foldableCount++;
        const id = `foldable-${this.foldableCount}`;
        const foldableBox = {
            //true 默认折叠
            id, title: title || `下载日志 ${this.foldableCount}`, content, folded: true
        };

        this.foldableData.push(foldableBox);
        this.renderFoldableBox(foldableBox);
        return id;
    }

    // 渲染折叠框
    renderFoldableBox(foldableBox) {
        const foldableContainer = document.getElementById('foldableContainer');

        const boxElement = document.createElement('div');
        boxElement.className = `foldable-box ${foldableBox.folded ? 'folded' : ''}`;
        boxElement.id = foldableBox.id;

        // 创建标题栏
        const header = document.createElement('div');
        header.className = 'foldable-header';
        header.innerHTML = `
          <div class="foldable-title">${foldableBox.title}</div>
          <div class="foldable-actions">
            <button class="foldable-btn btn-view" style="display: inline-block;margin-right: 3px" title="查看日志">👁️</button> 
            <span class="fold-icon"  style="display: inline-block;margin-right: 2px" title="折叠">▼</span>
            <button class="foldable-btn btn-delete" title="删除">🗑️</button>
          </div>
        `;

        // 创建内容区域
        const content = document.createElement('div');
        content.className = 'foldable-content';

        // 将日志内容添加到内容区域
        const lines = foldableBox.content.split('\n');
        lines.forEach(line => {
            if (line.trim()) {
                const lineDiv = document.createElement('div');
                lineDiv.className = 'foldable-log-line';
                lineDiv.textContent = line;
                content.appendChild(lineDiv);
            }
        });

        boxElement.appendChild(header);
        boxElement.appendChild(content);
        foldableContainer.appendChild(boxElement);

        // 添加事件监听
        header.addEventListener('click', (e) => {
            if (!e.target.classList.contains('btn-view') && !e.target.classList.contains('btn-delete')) {
                this.toggleFoldable(foldableBox.id);
            }
        });

        boxElement.querySelector('.btn-view').addEventListener('click', (e) => {
            e.stopPropagation();
            this.viewFoldableLogs(foldableBox.content);
        });

        boxElement.querySelector('.btn-delete').addEventListener('click', (e) => {
            e.stopPropagation();
            this.deleteFoldable(foldableBox.id);
        });
    }

    // 切换折叠状态
    toggleFoldable(id) {
        const index = this.foldableData.findIndex(box => box.id === id);
        if (index !== -1) {
            this.foldableData[index].folded = !this.foldableData[index].folded;
            const boxElement = document.getElementById(id);
            boxElement.classList.toggle('folded');
        }
    }

    // 查看折叠框中的日志
    viewFoldableLogs(content) {
        // 清空当前日志
        this.logData = [];
        this.filteredData = [];

        // 解析并加载折叠框中的日志
        const parsed = this.parser.parseChunk(content, 1, true);
        this.logData = parsed;

        // 更新统计信息
        this.threadMap = new Map();
        this.levelCounts = {TRACE: 0, DEBUG: 0, INFO: 0, WARN: 0, ERROR: 0, FATAL: 0};

        parsed.forEach(line => {
            if (!this.threadMap.has(line.thread)) {
                this.threadMap.set(line.thread, 0);
            }
            this.threadMap.set(line.thread, this.threadMap.get(line.thread) + 1);
            this.levelCounts[line.level] = (this.levelCounts[line.level] || 0) + 1;
        });

        this.updateStats();
        this.renderThreadsList();
        this.renderVisibleLines();
    }

    // 删除折叠框
    deleteFoldable(id) {
        const index = this.foldableData.findIndex(box => box.id === id);
        if (index !== -1) {
            this.foldableData.splice(index, 1);
            const boxElement = document.getElementById(id);
            if (boxElement) {
                boxElement.remove();
            }
        }
    }


    initStorageEvents() {
        // 确保所有需要的元素都已正确获取
        if (!this.elements.storagePanel || !this.elements.toggleStorageBtn || !this.elements.closeStoragePanelBtn || !this.elements.loadSelectedStorageBtn) {
            console.error('Storage panel elements not found');
            return;
        }
        // 确保清空按钮存在
        const clearBtn = this.elements.storagePanel.querySelector('.btn-clear-storage');
        if (!clearBtn) {
            console.error('Clear storage button not found');
            return;
        }

        // 切换存储面板显示
        this.elements.toggleStorageBtn.addEventListener('click', () => {
            console.log('===>111')
            const isHidden = this.elements.storagePanel.style.display === 'none';
            this.elements.storagePanel.style.display = isHidden ? 'flex' : 'none';
            if (isHidden) {
                this.renderStorageList(); // 确保每次展开都重新渲染
            }
        });

        this.elements.closeStoragePanelBtn.addEventListener('click', () => {
            this.elements.storagePanel.style.display = 'none';
        });

        this.elements.loadSelectedStorageBtn.addEventListener('click', () => {
            const selectedItem = document.querySelector('.storage-item input[type="radio"]:checked');
            if (selectedItem) {
                const id = parseInt(selectedItem.closest('.storage-item').dataset.id);
                this.viewStoredLog(id);
                this.elements.storagePanel.style.display = 'none';
            } else {
                alert('请先选择一个暂存日志');
            }
        });

        // 清空存储按钮事件
        if (clearBtn) {
            clearBtn.addEventListener('click', () => {
                if (confirm('确定要清空所有暂存日志吗？')) {
                    localStorage.removeItem('storedLogs');
                    this.renderStorageList();
                }
            });
        }
    }


    renderStorageList() {
        // 从 localStorage 获取数据
        let storedLogs = [];
        try {
            storedLogs = JSON.parse(localStorage.getItem('storedLogs') || '[]');
        } catch (e) {
            console.error('解析存储日志失败:', e);
            storedLogs = [];
        }

        // 清理过期日志
        const now = Date.now();
        const validLogs = storedLogs.filter(log => {
            // 确保日志对象结构正确
            return log && log.id && log.content && log.expireAt > now;
        });

        // 如果有变化，更新存储
        if (validLogs.length !== storedLogs.length) {
            localStorage.setItem('storedLogs', JSON.stringify(validLogs));
        }

        // 渲染列表
        const storageList = this.elements.storagePanel.querySelector('.storage-list');
        if (!storageList) return;

        if (validLogs.length === 0) {
            storageList.innerHTML = '<div class="storage-empty">暂无暂存日志</div>';
            return;
        }

        // 构建列表HTML（添加单选按钮）
        console.log('===>', validLogs)
        storageList.innerHTML = validLogs.map(log => `
        <div class="storage-item" data-id="${log.id}">
            <input type="radio" name="storedLog" id="log-${log.id}">
            <label for="log-${log.id}" class="storage-info">
                <div class="storage-desc">${log.description || '未命名日志'}</div>
                <div class="storage-meta">
                    <span class="storage-time">${log.timestamp || new Date(log.id).toLocaleString()}</span>
                    <span class="storage-size">${Math.ceil((log.content?.length || 0) / 1024)} KB</span>
                </div>
            </label>
            <div class="storage-actions">
                <button class="btn btn-view-storage">查看</button>
                <button class="btn btn-delete-storage">删除</button>
            </div>

        </div>
    `).join('');


        // 添加事件监听
        document.querySelectorAll('.btn-view-storage').forEach(btn => {
            btn.addEventListener('click', (e) => {
                const id = parseInt(e.target.closest('.storage-item').dataset.id);
                this.viewStoredLog(id);
            });
        });

        document.querySelectorAll('.btn-delete-storage').forEach(btn => {
            btn.addEventListener('click', (e) => {
                const id = parseInt(e.target.closest('.storage-item').dataset.id);
                this.deleteStoredLog(id);
            });
        });
    }

    initFullscreen() {
        this.fullscreenBtn = document.getElementById('fullscreenBtn');
        this.logsPanel = document.querySelector('.logs-panel');

        this.fullscreenBtn.addEventListener('click', () => {
            this.toggleFullscreen();
        });

        // 监听 ESC 键退出全屏
        document.addEventListener('keydown', (e) => {
            if (e.key === 'Escape' && this.logsPanel.classList.contains('fullscreen')) {
                this.exitFullscreen();
            }
        });
    }

    toggleFullscreen() {
        if (this.logsPanel.classList.contains('fullscreen')) {
            this.exitFullscreen();
        } else {
            this.enterFullscreen();
        }
    }

    enterFullscreen() {
        this.logsPanel.classList.add('fullscreen');
        document.body.classList.add('logs-fullscreen');
        this.fullscreenBtn.innerHTML = `
        <svg viewBox="0 0 24 24" width="16" height="16">
            <path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"/>
        </svg>
    `;
        this.fullscreenBtn.title = "退出全屏";
        this.renderVisibleLines(); // 重新渲染以适应新尺寸

        const title = this.logsPanel.querySelector('.panel-header span');
        if (title) title.textContent = '日志查看器 (全屏模式)';
    }

    exitFullscreen() {
        this.logsPanel.classList.remove('fullscreen');
        document.body.classList.remove('logs-fullscreen');
        this.fullscreenBtn.innerHTML = `
        <svg viewBox="0 0 24 24" width="16" height="16">
            <path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/>
        </svg>
    `;
        this.fullscreenBtn.title = "全屏";
        this.renderVisibleLines(); // 重新渲染以适应新尺寸

        const title = this.logsPanel.querySelector('.panel-header span');
        if (title) title.textContent = '日志查看器';
    }


    // 初始化下载相关事件
    initDownloadEvents() {
        this.downloadBtn.addEventListener('click', () => this.downloadLogs());
        this.manageDownloadBtn.addEventListener('click', () => {
            this.downloadConfigModal.style.display = 'block';
        });

        this.saveConfigBtn.addEventListener('click', () => this.saveDownloadConfig());

        // 关闭下载配置模态框
        document.querySelector('.download-close').addEventListener('click', () => {
            this.downloadConfigModal.style.display = 'none';
        });

        // 点击模态框外部关闭
        window.addEventListener('click', (e) => {
            if (e.target === this.downloadConfigModal) {
                this.downloadConfigModal.style.display = 'none';
            }
        });
    }

    // 从localStorage加载下载配置
    loadDownloadConfigs() {
        const configs = localStorage.getItem('logDownloadConfigs');
        if (configs) {
            this.downloadConfigs = JSON.parse(configs);
            this.renderDownloadSelect();
            this.renderDownloadConfigList();
        }
    }

    // 保存下载配置到localStorage
    saveDownloadConfig() {
        const alias = this.configAlias.value.trim();
        const url = this.configUrl.value.trim();

        if (!alias || !url) {
            alert('别名和URL不能为空');
            return;
        }

        // 检查是否已存在相同别名
        const existingIndex = this.downloadConfigs.findIndex(c => c.alias === alias);
        if (existingIndex !== -1) {
            this.downloadConfigs[existingIndex].url = url;
        } else {
            this.downloadConfigs.push({alias, url});
        }

        localStorage.setItem('logDownloadConfigs', JSON.stringify(this.downloadConfigs));
        this.renderDownloadSelect();
        this.renderDownloadConfigList();

        // 清空输入
        this.configAlias.value = '';
        this.configUrl.value = '';
    }

    // 渲染下载配置下拉菜单
    renderDownloadSelect() {
        this.downloadSelect.innerHTML = '<option value="">选择下载配置</option>';

        this.downloadConfigs.forEach(config => {
            const option = document.createElement('option');
            option.value = config.alias;
            option.textContent = config.alias;
            this.downloadSelect.appendChild(option);
        });
    }

    // 渲染下载配置列表
    renderDownloadConfigList() {
        this.downloadConfigList.innerHTML = '';

        this.downloadConfigs.forEach((config, index) => {
            const configItem = document.createElement('div');
            configItem.className = 'config-item';

            const configInfo = document.createElement('div');
            configInfo.className = 'config-info';
            configInfo.innerHTML = `<strong>${config.alias}</strong><div class="config-url">${config.url}</div>`;

            const configActions = document.createElement('div');
            configActions.className = 'config-actions';

            const editBtn = document.createElement('button');
            editBtn.className = 'btn btn-edit';
            editBtn.textContent = '编辑';
            editBtn.addEventListener('click', () => {
                this.configAlias.value = config.alias;
                this.configUrl.value = config.url;
            });

            const deleteBtn = document.createElement('button');
            deleteBtn.className = 'btn btn-delete';
            deleteBtn.textContent = '删除';
            deleteBtn.addEventListener('click', () => {
                this.downloadConfigs.splice(index, 1);
                localStorage.setItem('logDownloadConfigs', JSON.stringify(this.downloadConfigs));
                this.renderDownloadSelect();
                this.renderDownloadConfigList();
            });

            configActions.appendChild(editBtn);
            configActions.appendChild(deleteBtn);
            configItem.appendChild(configInfo);
            configItem.appendChild(configActions);
            this.downloadConfigList.appendChild(configItem);
        });
    }

    // 下载日志
    async downloadLogs() {
        const selectedAlias = this.downloadSelect.value;
        if (!selectedAlias) {
            alert('请选择下载配置');
            return;
        }

        const config = this.downloadConfigs.find(c => c.alias === selectedAlias);
        if (!config || !config.url) {
            alert('无效的下载配置');
            return;
        }

        try {
            // 显示下载进度
            this.downloadProgress.style.display = 'block';
            this.downloadProgress.textContent = '下载中...';

            // 使用GET请求下载文本格式日志
            console.log('===>下载的地址 ', config.url)
            const response = await fetch(config.url);

            if (!response.ok) {
                throw new Error(`下载失败: ${response.status} ${response.statusText}`);
            }

            // 直接获取文本内容
            const text = await response.text();

            // 创建折叠框并添加下载的日志
            this.createFoldableBox(`下载日志: ${config.alias} 地址: ${config.url}` , text);

            // 追加日志到现有数据
            // this.appendDownloadedLogs(text, config.alias);

            this.downloadProgress.textContent = '下载完成！';
            setTimeout(() => {
                this.downloadProgress.style.display = 'none';
            }, 2000);

        } catch (error) {
            console.error('下载失败:', error);
            this.downloadProgress.textContent = `下载失败: ${error.message}`;
            setTimeout(() => {
                this.downloadProgress.style.display = 'none';
            }, 3000);
        }
    }

    // 追加下载的日志数据
    // 追加下载的日志数据
    appendDownloadedLogs(logText, alias) {
        if (!logText.trim()) return;

        // 添加分隔行（3行空格）
        const separatorLines = Array(3).fill().map((_, i) => ({
            content: `------ 下载日志[${alias}] ------`,
            thread: 'SYSTEM',
            level: 'INFO',
            lineNumber: this.logData.length + i + 1,
            isSeparator: true,
            alignLeft: true  // 新增属性，标识需要左对齐
        }));

        // 解析新日志
        const parsedLines = this.parser.parseChunk(logText, this.logData.length + separatorLines.length + 1, true);

        // 合并数据
        const allNewLines = [...separatorLines, ...parsedLines];

        // 更新日志数据
        this.logData.push(...allNewLines);

        // 更新统计信息
        allNewLines.forEach(line => {
            if (!line.isSeparator) {
                if (!this.threadMap.has(line.thread)) {
                    this.threadMap.set(line.thread, 0);
                }
                this.threadMap.set(line.thread, this.threadMap.get(line.thread) + 1);
                this.levelCounts[line.level] = (this.levelCounts[line.level] || 0) + 1;
            }
        });

        // 更新UI
        this.updateStats();
        this.renderThreadsList();
        this.renderVisibleLines();

        // 滚动到底部
        this.elements.logViewport.scrollTop = this.elements.logViewport.scrollHeight;
    }

    initResizable() {
        const handle = document.querySelector('.resize-handle');
        const threadsPanel = document.getElementById('threadsPanel');
        let isDragging = false;

        // 1. 鼠标按下事件
        handle.addEventListener('mousedown', (e) => {
            isDragging = true;
            document.body.style.cursor = 'col-resize';
            document.addEventListener('mousemove', resize);
            document.addEventListener('mouseup', stopResize);
        });

        // 2. 拖动处理函数
        const resize = (e) => {
            if (!isDragging) return;

            const containerRect = threadsPanel.parentNode.getBoundingClientRect();
            const newWidth = e.clientX - containerRect.left;

            // 应用约束条件
            const minWidth = 200;
            const maxWidth = containerRect.width * 0.8;

            threadsPanel.style.width = `${Math.min(maxWidth, Math.max(minWidth, newWidth))}px`;
        };

        // 3. 停止拖动处理
        const stopResize = () => {
            isDragging = false;
            document.body.style.cursor = '';
            document.removeEventListener('mousemove', resize);
            document.removeEventListener('mouseup', stopResize);

            // 触发重新布局
            setTimeout(() => this.renderVisibleLines(), 0);
        };

        // 4. 窗口大小变化时的防抖处理 - 新增代码
        let resizeTimer;
        window.addEventListener('resize', () => {
            clearTimeout(resizeTimer);
            resizeTimer = setTimeout(() => {
                this.renderVisibleLines();
            }, 100);
        });
    }


    /*========websocket日志========*/
    initWebSocket() {
        this.connectBtn.addEventListener('click', () => {
            if (this.socket && this.socket.readyState === WebSocket.OPEN) {
                this.socket.close();
                this.connectBtn.textContent = '连接日志服务';
                return;
            }

            const url = this.serverUrl.value.trim();
            if (!url) return;

            this.socket = new WebSocket(url);

            this.socket.onopen = () => {
                this.connectBtn.classList.add('connected');
                this.connectBtn.classList.remove('disconnected');

                this.connectBtn.textContent = '断开连接';
                console.log('WebSocket连接已建立');
                this.clearLogs();
            };

            this.socket.onmessage = (event) => {
                this.processLogMessage(event.data);
            };

            this.socket.onclose = () => {
                this.connectBtn.classList.add('disconnected');
                this.connectBtn.classList.remove('connected');

                this.connectBtn.textContent = '连接日志服务';
                console.log('WebSocket连接已关闭');
            };

            this.socket.onerror = (error) => {
                console.error('WebSocket错误:', error);
                alert('连接失败: ' + error.message);
            };
        });
    }

    processLogMessage(logData) {
        // 解析日志数据
        const parsed = this.parser.parseChunk(logData + '\n', this.logData.length + 1, false);

        if (parsed.length > 0) {
            // 更新统计信息
            parsed.forEach(line => {
                if (!this.threadMap.has(line.thread)) {
                    this.threadMap.set(line.thread, 0);
                }
                this.threadMap.set(line.thread, this.threadMap.get(line.thread) + 1);
                this.levelCounts[line.level] = (this.levelCounts[line.level] || 0) + 1;
            });

            // 添加到日志数据
            this.logData.push(...parsed);

            // 更新UI
            this.updateStats();
            this.renderThreadsList();
            this.renderVisibleLines();

            // 自动滚动到底部
            this.elements.logViewport.scrollTop = this.elements.logViewport.scrollHeight;
        }
    }

    clearLogs() {
        this.logData = [];
        this.threadMap = new Map();
        this.levelCounts = {TRACE: 0, DEBUG: 0, INFO: 0, WARN: 0, ERROR: 0, FATAL: 0};
        this.updateStats();
        this.renderThreadsList();
        this.elements.logContent.innerHTML = '';
    }

    /*========websocket日志========*/


    initPasteEvents() {
        // 打开粘贴模态框
        this.pasteBtn.addEventListener('click', () => {
            this.pasteModal.style.display = 'block';
            this.pasteArea.focus();
        });

        // 关闭模态框
        this.closeModal.addEventListener('click', () => {
            this.pasteModal.style.display = 'none';
        });

        // 提交粘贴内容
        this.submitPaste.addEventListener('click', () => {
            const content = this.pasteArea.value.trim();
            if (content) {
                this.processPastedContent(content);
                this.pasteModal.style.display = 'none';
                this.pasteArea.value = '';
            }
        });

        // 点击模态框外部关闭
        window.addEventListener('click', (e) => {
            if (e.target === this.pasteModal) {
                this.pasteModal.style.display = 'none';
            }
        });
    }

    processPastedContent(content) {
        // 重置状态
        this.file = null;
        this.logData = [];
        this.threadMap = new Map();
        this.levelCounts = {
            TRACE: 0, DEBUG: 0, INFO: 0, WARN: 0, ERROR: 0, FATAL: 0
        };

        // 显示处理进度
        this.showProgress();

        // 模拟文件处理
        const lines = content.split('\n');
        let lineNumber = 0;

        // 分批处理避免阻塞UI
        const processBatch = () => {
            const batchSize = 1000;
            const batch = lines.splice(0, batchSize);

            batch.forEach((line, i) => {
                const parsed = this.parser.parseChunk(line + '\n', lineNumber + 1, i === batch.length - 1 && lines.length === 0);
                if (parsed.length > 0) {
                    lineNumber += parsed.length;
                    this.logData.push(...parsed);

                    // 更新统计信息
                    parsed.forEach(line => {
                        if (!this.threadMap.has(line.thread)) {
                            this.threadMap.set(line.thread, 0);
                        }
                        this.threadMap.set(line.thread, this.threadMap.get(line.thread) + 1);
                        this.levelCounts[line.level] = (this.levelCounts[line.level] || 0) + 1;
                    });
                }
            });

            // 更新进度
            this.updateProgress((lineNumber / (lineNumber + lines.length)) * 100, lineNumber);

            if (lines.length > 0) {
                setTimeout(processBatch, 0);
            } else {
                this.hideProgress();
                this.updateStats();
                this.renderThreadsList();
                this.renderVisibleLines();
            }
        };

        processBatch();
    }

    initElements() {
        this.elements = {
            fileInput: document.getElementById('fileInput'),
            loadBtn: document.getElementById('loadBtn'),
            logSearch: document.getElementById('logSearch'),
            threadSearch: document.getElementById('threadSearch'),
            searchBtn: document.getElementById('searchBtn'),
            threadsList: document.getElementById('threadsList'),
            logViewport: document.getElementById('logViewport'),
            logContent: document.getElementById('logContent'),
            progressContainer: document.getElementById('progressContainer'),
            progressPercent: document.getElementById('progressPercent'),
            loadedLines: document.getElementById('loadedLines'),
            totalLines: document.getElementById('totalLines'),
            threadCount: document.getElementById('threadCount'),
            debugCount: document.getElementById('debugCount'),
            infoCount: document.getElementById('infoCount'),
            warnCount: document.getElementById('warnCount'),
            statItems: document.querySelectorAll('.clickable-stat'),
            copyBtn: document.getElementById('copyBtn'),
            storageBtn: document.getElementById('storageBtn'),
            contextLinesInput: document.getElementById('contextLines'),
            errorCount: document.getElementById('errorCount'), // // 添加存储面板相关元素
            // storagePanel:document.getElementById('storagePanel'),
            // toggleStorageBtn:document.getElementById('toggleStorageBtn'),
            //
            // loadSelectedStorageBtn:document.getElementById('loadSelectedStorage'),
            // closeStoragePanelBtn:document.getElementById('closeStoragePanel')
        };

        this.elements.storagePanel = document.getElementById('storagePanel');
        this.elements.toggleStorageBtn = document.getElementById('toggleStorageBtn');
        this.elements.loadSelectedStorageBtn = document.getElementById('loadSelectedStorage');
        this.elements.closeStoragePanelBtn = document.getElementById('closeStoragePanel');
    }

    initEvents() {
        this.elements.loadBtn.addEventListener('click', () => this.elements.fileInput.click());
        this.elements.fileInput.addEventListener('change', (e) => this.handleFileSelect(e));
        this.elements.searchBtn.addEventListener('click', () => this.handleSearch());
        this.elements.logSearch.addEventListener('keyup', (e) => {
            if (e.key === 'Enter') this.handleSearch();
        });
        this.elements.threadSearch.addEventListener('input', () => {
            this.currentThreadSearch = this.elements.threadSearch.value.trim();
            this.renderThreadsList();
        });
        // 优化滚动事件监听
        this.elements.logViewport.addEventListener('scroll', () => {
            const now = performance.now();
            const scrollTop = this.elements.logViewport.scrollTop;

            // 节流处理滚动事件
            if (now - this.lastRenderTime < 50 && Math.abs(scrollTop - this.lastScrollTop) < 100) {
                return;
            }

            this.lastScrollTop = scrollTop;
            this.lastRenderTime = now;

            // 使用requestAnimationFrame确保流畅渲染
            if (this.renderQueue) {
                cancelAnimationFrame(this.renderQueue);
            }

            this.renderQueue = requestAnimationFrame(() => {
                this.handleScroll();
                this.renderQueue = null;
            });
        });

        // 添加统计项点击事件
        this.elements.statItems.forEach(item => {
            item.addEventListener('click', () => {
                this.currentLevel = item.getAttribute('data-level');
                this.updateActiveStatItem();
                this.applyFilters();
            });
        });

        // 添加上下文行数变化事件
        this.elements.contextLinesInput.addEventListener('change', () => {
            this.contextLines = parseInt(this.elements.contextLinesInput.value) || 0;
            this.applyFilters();
        });

        // 一键复制
        this.elements.copyBtn.addEventListener('click', () => this.copyLogs());

        //暂存
        this.elements.storageBtn.addEventListener('click', () => this.storeCurrentLogs());
        this.elements.storagePanel.querySelector('.btn-clear-storage').addEventListener('click', () => this.clearStorage());


    }

    // 新增复制方法
    copyLogs() {
        const data = this.filteredData.length ? this.filteredData : this.logData;
        const logText = data.map(line => line.content).join('\n');

        navigator.clipboard.writeText(logText).then(() => {
            this.showCopySuccess();
        }).catch(err => {
            console.error('复制失败:', err);
            alert('复制失败，请手动选择文本后复制');
        });
    }

    showCopySuccess() {
        const tooltip = document.createElement('div');
        tooltip.className = 'copy-tooltip';
        tooltip.textContent = '已复制到剪贴板！';
        this.elements.copyBtn.appendChild(tooltip);

        alert("已复制到剪贴板！")

        setTimeout(() => {
            tooltip.remove();
        }, 2000);
    }

    storeCurrentLogs() {
        const description = prompt('请输入本次暂存日志的描述:', '');
        if (description === null) return;

        const data = this.filteredData.length ? this.filteredData : this.logData;
        const logText = data.map(line => line.content).join('\n');

        const storedLogs = JSON.parse(localStorage.getItem('storedLogs') || '[]');
        const id = Date.now();

        storedLogs.push({
            id,
            description,
            content: logText,
            timestamp: new Date().toLocaleString(),
            expireAt: Date.now() + 5 * 24 * 60 * 60 * 1000 // 5 * 24小时后过期
        });

        localStorage.setItem('storedLogs', JSON.stringify(storedLogs));
        this.renderStorageList();
        this.showStorageSuccess();
    }

    clearStorage() {
        if (confirm('确定要清空所有暂存日志吗？')) {
            localStorage.removeItem('storedLogs');
            this.renderStorageList();
        }
    }


    viewStoredLog(id) {
        const storedLogs = JSON.parse(localStorage.getItem('storedLogs') || '[]');
        const log = storedLogs.find(item => item.id === id);

        if (log) {
            // 清空当前日志
            this.logData = [];
            this.filteredData = [];

            // 解析并加载暂存日志
            const parsed = this.parser.parseChunk(log.content, 1, true);
            this.logData = parsed;

            // 更新统计信息
            this.threadMap = new Map();
            this.levelCounts = {TRACE: 0, DEBUG: 0, INFO: 0, WARN: 0, ERROR: 0, FATAL: 0};

            parsed.forEach(line => {
                if (!this.threadMap.has(line.thread)) {
                    this.threadMap.set(line.thread, 0);
                }
                this.threadMap.set(line.thread, this.threadMap.get(line.thread) + 1);
                this.levelCounts[line.level] = (this.levelCounts[line.level] || 0) + 1;
            });

            this.updateStats();
            this.renderThreadsList();
            this.renderVisibleLines();

            alert(`已加载暂存日志: ${log.description}`);
        }
    }

    deleteStoredLog(id) {
        const storedLogs = JSON.parse(localStorage.getItem('storedLogs') || '[]');
        const updatedLogs = storedLogs.filter(log => log.id !== id);

        localStorage.setItem('storedLogs', JSON.stringify(updatedLogs));
        this.renderStorageList();
    }

    showStorageSuccess() {
        const tooltip = document.createElement('div');
        tooltip.className = 'storage-tooltip';
        tooltip.textContent = '日志已暂存！';
        this.elements.storageBtn.appendChild(tooltip);

        setTimeout(() => {
            tooltip.remove();
        }, 2000);
    }


    updateActiveStatItem() {
        this.elements.statItems.forEach(item => {
            const isActive = item.getAttribute('data-level') === this.currentLevel;
            item.classList.toggle('active', isActive);
        });
    }

    async handleFileSelect(event) {
        const file = event.target.files[0];
        if (!file) return;

        this.file = file;
        this.logData = [];
        this.threadMap = new Map();
        this.levelCounts = {
            TRACE: 0, DEBUG: 0, INFO: 0, WARN: 0, ERROR: 0, FATAL: 0
        };

        this.showProgress();
        await this.processFile(file);
        this.hideProgress();
        this.updateStats();
        this.renderThreadsList();
        this.renderVisibleLines();
    }

    async processFile(file) {
        const CHUNK_SIZE = 5 * 1024 * 1024; // 5MB
        let offset = 0;
        let lineNumber = 0;

        while (offset < file.size) {
            const chunk = file.slice(offset, offset + CHUNK_SIZE);
            const text = await this.readChunk(chunk);

            const isLastChunk = offset + CHUNK_SIZE >= file.size;
            const parsedLines = this.parser.parseChunk(text, lineNumber + 1, isLastChunk);

            // 更新统计信息
            parsedLines.forEach(line => {
                if (!this.threadMap.has(line.thread)) {
                    this.threadMap.set(line.thread, 0);
                }
                this.threadMap.set(line.thread, this.threadMap.get(line.thread) + 1);

                this.levelCounts[line.level] = (this.levelCounts[line.level] || 0) + 1;
            });

            lineNumber += parsedLines.length;
            this.logData.push(...parsedLines);

            // 更新进度
            offset += CHUNK_SIZE;
            const percent = Math.min(100, (offset / file.size) * 100);
            this.updateProgress(percent, lineNumber);

            // 让浏览器有机会渲染
            await new Promise(resolve => setTimeout(resolve, 0));
        }
    }

    readChunk(chunk) {
        return new Promise((resolve) => {
            const reader = new FileReader();
            reader.onload = (e) => resolve(e.target.result);
            reader.readAsText(chunk);
        });
    }

    showProgress() {
        this.elements.progressContainer.style.display = 'block';
    }

    hideProgress() {
        this.elements.progressContainer.style.display = 'none';
    }

    updateProgress(percent, lines) {
        this.elements.progressPercent.textContent = percent.toFixed(1);
        this.elements.loadedLines.textContent = lines.toLocaleString();
    }

    updateStats() {
        this.elements.totalLines.textContent = this.logData.length.toLocaleString();
        this.elements.debugCount.textContent = this.levelCounts.DEBUG.toLocaleString();
        this.elements.infoCount.textContent = this.levelCounts.INFO.toLocaleString();
        this.elements.warnCount.textContent = this.levelCounts.WARN.toLocaleString();
        this.elements.errorCount.textContent = this.levelCounts.ERROR.toLocaleString();
    }

    renderThreadsList() {
        const threads = Array.from(this.threadMap.entries())
            .sort((a, b) => b[1] - a[1])
            .filter(([thread]) => this.currentThreadSearch === '' || thread.toLowerCase().includes(this.currentThreadSearch.toLowerCase()));

        let html = '';
        html += `<div class="thread-item" data-thread="all">
            <div class="thread-info">
                <span>所有线程</span>
                <span class="thread-count">${this.logData.length.toLocaleString()}</span>
            </div>
            <div class="thread-actions">
                <button class="btn btn-download" data-thread="all">下载</button>
            </div>
        </div>`;

        threads.forEach(([thread, count]) => {
            html += `
                <div class="thread-item" data-thread="${thread}">
                    <div class="thread-info">
                        <span>${thread}</span>
                        <span class="thread-count">${count.toLocaleString()}</span>
                    </div>
                    <div class="thread-actions">
                        <button class="btn btn-download" data-thread="${thread}">下载</button>
                    </div>
                </div>
            `;
        });

        this.elements.threadsList.innerHTML = html;

        // 添加线程点击事件
        document.querySelectorAll('.thread-item').forEach(item => {
            item.addEventListener('click', (e) => {
                // 如果是点击下载按钮，则不切换线程
                if (e.target.classList.contains('btn-download')) {
                    return;
                }

                // 移除所有active类
                document.querySelectorAll('.thread-item').forEach(el => {
                    el.classList.remove('active');
                });

                // 添加当前active类
                e.currentTarget.classList.add('active');

                // 设置当前线程
                this.currentThread = e.currentTarget.getAttribute('data-thread');
                this.applyFilters();
            });
        });

        // 添加下载事件
        document.querySelectorAll('.btn-download').forEach(btn => {
            btn.addEventListener('click', (e) => {
                e.stopPropagation();
                const thread = e.target.getAttribute('data-thread');
                this.downloadThreadLogs(thread);
            });
        });

        // 默认选中"所有线程"
        const allThreadItem = document.querySelector('.thread-item[data-thread="all"]');
        if (allThreadItem) {
            allThreadItem.classList.add('active');
        }
    }

    downloadThreadLogs(thread) {
        const threadLogs = thread === 'all' ? this.logData : this.logData.filter(line => line.thread === thread);

        const content = threadLogs.map(line => line.content).join('\n');
        this.downloadFile(content, thread === 'all' ? 'all_logs.txt' : `logs_${thread}.txt`);
    }

    downloadFile(content, filename) {
        const blob = new Blob([content], {type: 'text/plain'});
        const url = URL.createObjectURL(blob);

        const a = document.createElement('a');
        a.href = url;
        a.download = filename;
        document.body.appendChild(a);
        a.click();

        setTimeout(() => {
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
        }, 0);
    }

    handleSearch() {
        this.currentSearch = this.elements.logSearch.value.trim();
        this.applyFilters();
    }


    createSeparatorLine() {
        return {
            content: '', thread: '', level: '', lineNumber: -1, isContextSeparator: true
        };
    }

    applyFilters() {
        // 先应用线程和级别过滤
        let filteredByThreadAndLevel = this.logData;
        if (this.currentThread !== 'all') {
            filteredByThreadAndLevel = filteredByThreadAndLevel.filter(line => line.thread === this.currentThread);
        }
        if (this.currentLevel !== 'all') {
            filteredByThreadAndLevel = filteredByThreadAndLevel.filter(line => line.level === this.currentLevel);
        }

        // 如果没有搜索词，直接返回
        if (!this.currentSearch) {
            this.filteredData = filteredByThreadAndLevel;
            this.renderVisibleLines();
            return;
        }

        const context = this.contextLines;
        const resultBlocks = [];
        let lastBlockEnd = -Infinity;

        // 遍历查找匹配行
        filteredByThreadAndLevel.forEach((line, index) => {
            if (line.content.includes(this.currentSearch)) {
                // 计算当前块的上下文范围
                const blockStart = Math.max(0, index - context, lastBlockEnd + 1);
                const blockEnd = Math.min(filteredByThreadAndLevel.length - 1, index + context);

                // 如果不是第一个块且与前一个块不重叠，添加分隔行
                if (resultBlocks.length > 0 && blockStart > lastBlockEnd + 1) {
                    resultBlocks.push(this.createSeparatorLine());
                }

                // 添加当前块的行
                for (let i = blockStart; i <= blockEnd; i++) {
                    const currentLine = filteredByThreadAndLevel[i];
                    resultBlocks.push({
                        ...currentLine,
                        isMatch: currentLine.content.includes(this.currentSearch),
                        isContextLine: !currentLine.content.includes(this.currentSearch)
                    });
                }

                lastBlockEnd = blockEnd;
            }
        });

        this.filteredData = resultBlocks.length > 0 ? resultBlocks : [];


        // 在过滤完成后检查结果
        if (this.currentSearch && this.filteredData.length === 0) {
            this.showNoResultsAlert();
        }

        this.renderVisibleLines();
    }

    // 新增方法
    showNoResultsAlert() {
        const alertBox = document.createElement('div');
        alertBox.className = 'search-alert';
        alertBox.innerHTML = `
        <div class="alert-content">
            <p>没有找到匹配的日志内容</p>
            <button class="btn btn-close-alert">确定</button>
        </div>
    `;
        document.body.appendChild(alertBox);

        alertBox.querySelector('.btn-close-alert').addEventListener('click', () => {
            alertBox.remove();
        });
    }

    handleScroll() {
        const scrollTop = this.elements.logViewport.scrollTop;
        const viewportHeight = this.elements.logViewport.clientHeight;

        // 计算需要渲染的行范围（扩大缓冲区）
        const startIdx = Math.max(0, Math.floor(scrollTop / this.lineHeight) - this.renderBuffer);
        const endIdx = Math.min(startIdx + this.visibleLines + this.renderBuffer * 2, (this.filteredData.length || this.logData.length));

        // 设置内容区域高度
        const totalHeight = (this.filteredData.length || this.logData.length) * this.lineHeight;
        this.elements.logContent.style.height = `${totalHeight}px`;

        // 计算要渲染的行
        const data = this.filteredData.length ? this.filteredData : this.logData;
        const linesToRender = data.slice(startIdx, endIdx);

        // 使用文档片段提高渲染性能
        const fragment = document.createDocumentFragment();

        linesToRender.forEach((line, idx) => {
            const top = (startIdx + idx) * this.lineHeight;

            let content = line.content;
            if (this.currentSearch) {
                const regex = new RegExp(this.escapeRegExp(this.currentSearch), 'gi');
                content = content.replace(regex, match => `<span class="highlight">${match}</span>`);
            }

            const lineDiv = document.createElement('div');
            lineDiv.className = `log-line level-${line.level}`;
            lineDiv.style.cssText = `top: ${top}px; height: ${this.lineHeight}px;`;
            lineDiv.innerHTML = content;


            // 添加分隔行特殊样式
            if (line.isSeparator) {
                lineDiv.classList.add('separator-line');
                lineDiv.textContent = line.content; // 直接显示文本内容
            }


            fragment.appendChild(lineDiv);
        });

        // 一次性更新DOM
        this.elements.logContent.innerHTML = '';
        this.elements.logContent.appendChild(fragment);
    }


    renderVisibleLines() {
        if (!this.logData.length) return;

        // 在全屏状态下调整可视区域计算
        if (this.logsPanel.classList.contains('fullscreen')) {
            this.visibleLines = Math.floor(window.innerHeight / this.lineHeight) + 2;
        } else {
            this.visibleLines = 50; // 默认值
        }


        // 1. 获取视口尺寸和滚动位置（保留）
        const scrollTop = this.elements.logViewport.scrollTop;
        const viewportHeight = this.elements.logViewport.clientHeight;
        const viewportWidth = this.elements.logViewport.clientWidth;

        // 2. 计算渲染范围（保留缓冲区逻辑）
        const startIdx = Math.max(0, Math.floor(scrollTop / this.lineHeight) - this.renderBuffer);
        const endIdx = Math.min(startIdx + this.visibleLines + this.renderBuffer * 2, (this.filteredData.length || this.logData.length));

        // 3. 设置容器高度（保留）
        const totalLines = this.filteredData.length || this.logData.length;
        const totalHeight = totalLines * this.lineHeight;
        this.elements.logContent.style.height = `${totalHeight}px`;

        // 4. 获取要渲染的数据（保留过滤逻辑）
        const data = this.filteredData.length ? this.filteredData : this.logData;
        const linesToRender = data.slice(startIdx, endIdx);

        // 5. 计算最大行宽度（保留动态宽度测量）
        let maxLineWidth = viewportWidth;
        if (linesToRender.length > 0) {
            const measureEl = document.createElement('div');
            measureEl.style.position = 'absolute';
            measureEl.style.visibility = 'hidden';
            measureEl.style.whiteSpace = 'nowrap';
            measureEl.style.fontFamily = "'Courier New', monospace";
            measureEl.style.fontSize = '13px';
            measureEl.style.padding = '0 10px';
            document.body.appendChild(measureEl);

            linesToRender.forEach(line => {
                measureEl.textContent = line.content;
                const lineWidth = measureEl.offsetWidth;
                if (lineWidth > maxLineWidth) {
                    maxLineWidth = lineWidth + 20;
                }
            });
            document.body.removeChild(measureEl);
        }

        // 6. 设置容器宽度（保留）
        this.elements.logContent.style.width = `${maxLineWidth}px`;
        this.elements.logContent.style.minWidth = '100%';

        // 7. 渲染行（修复变量作用域问题）
        const fragment = document.createDocumentFragment();
        linesToRender.forEach((line, idx) => {
            const top = (startIdx + idx) * this.lineHeight;
            let content = line.content;

            // 保留搜索高亮逻辑
            if (this.currentSearch) {
                const regex = new RegExp(this.escapeRegExp(this.currentSearch), 'gi');
                content = content.replace(regex, match => `<span class="highlight">${match}</span>`);
            }

            // 修复点：明确声明 lineEl 变量
            const lineEl = document.createElement('div');
            lineEl.className = `log-line level-${line.level}`;
            lineEl.style.cssText = `
            position: absolute;
            top: ${top}px;
            height: ${this.lineHeight}px;
            line-height: ${this.lineHeight}px;
            white-space: nowrap;
            padding: 0 10px;
            min-width: ${maxLineWidth}px;
        `;
            lineEl.innerHTML = content;

            // 处理分隔行（优先处理）
            if (line.isSeparator) {
                const lineEl = document.createElement('div');
                lineEl.className = 'separator-line';
                lineEl.style.cssText = `
                position: absolute;
                top: ${top}px;
                height: ${this.lineHeight}px;
                line-height: ${this.lineHeight}px;
                white-space: nowrap;
                padding: 0 10px;
                min-width: ${maxLineWidth}px;
                text-align: left;
            `;
                lineEl.textContent = line.content;
                fragment.appendChild(lineEl);
                return; // 跳过后续处理
            }

            fragment.appendChild(lineEl);
        });

        // 8. 更新DOM（保留）
        this.elements.logContent.innerHTML = '';
        this.elements.logContent.appendChild(fragment);

        // 9. 自动滚动到底部（保留）
        const scrollThreshold = totalHeight - viewportHeight - 50;
        if (scrollTop > scrollThreshold && scrollTop > 0) {
            this.elements.logViewport.scrollTop = totalHeight - viewportHeight;
        }
    }

    escapeRegExp(string) {
        return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    }
}

// 初始化应用
document.addEventListener('DOMContentLoaded', () => {
    const logViewer = new LogViewer();
});
